﻿using System.Text;

namespace DimensionsHelper.LanguageService.Serialization;

internal sealed class SourceTextWriter
{
    private const char IndentationChar = ' ';
    private const int DefaultIndentation = 4;
    private readonly StringBuilder _builder = new();
    private int _indentLevel = -1;
    private int _indentSpaceCount = -1;


    public int IndentSpaceCount
    {
        get => _indentSpaceCount < 0 ? DefaultIndentation : _indentSpaceCount;
        set => _indentSpaceCount = value;
    }


    public int IndentLevel
    {
        get => _indentLevel < 0 ? 0 : _indentLevel;
        set => _indentLevel = value;
    }


    public void Append(char c)
    {
        _builder.Append(c);
    }


    public void Append(string text)
    {
        _builder.Append(text);
    }


    public void AppendIndentation()
    {
        if (IndentLevel > 0)
        {
            _builder.Append(IndentationChar, IndentLevel * IndentSpaceCount);
        }
    }

    public void AppendLine(char ch)
    {
        AppendIndentation();
        _builder.Append(ch);
        _builder.AppendLine();
    }


    public void AppendLine()
    {
        _builder.AppendLine();
    }


    public void AppendLine(string text)
    {
        if (IndentLevel == 0)
        {
            _builder.AppendLine(text);
            return;
        }

        ReadOnlySpan<char> span = text.AsSpan();
        bool isLastLine;
        do
        {
            ReadOnlySpan<char> line = GetLineFromSpan(ref span, out isLastLine);

            AppendIndentation();
            _builder.Append(line);
            _builder.AppendLine();
        } while (!isLastLine);
    }


    private static ReadOnlySpan<char> GetLineFromSpan(ref ReadOnlySpan<char> remaining, out bool isLastLine)
    {
        if (remaining.IsEmpty)
        {
            isLastLine = true;
            return default;
        }

        ReadOnlySpan<char> restText;

        int lfIndex = remaining.IndexOf('\n');
        // 最后一行
        if (lfIndex == -1)
        {
            lfIndex = remaining.Length;
            isLastLine = true;
            restText = default;
        }
        else
        {
            restText = remaining[(lfIndex + 1)..];
            isLastLine = false;
        }

        if (lfIndex > 0 && remaining[lfIndex - 1] == '\r')
        {
            lfIndex--;
        }

        ReadOnlySpan<char> nextLine = remaining[..lfIndex];
        remaining = restText;
        return nextLine;
    }


    public void Clear()
    {
        _builder.Clear();
        _indentLevel = 0;
    }


    public override string ToString()
    {
        return _builder.ToString();
    }
}